
#import "DataCenter.h"

@implementation DataCenter

//
// singleton
//
static DataCenter* globalDataCenter											= nil;

//
// database
//
@synthesize database														= _database;

//
// domain
//
@synthesize domain															= _domain;

//
// user id
//
@synthesize userId															= _userId;

//
// mail address
//
@synthesize mailAddress														= _mailAddress;

//
// pass key
//
@synthesize passKey															= _passKey;

//
// cookie nexusphp
//
@synthesize cookieNexusPhp													= _cookieNexusPhp;

//
// push config
//
@synthesize pushConfig														= _pushConfig;

//
// server list
//
@synthesize serverList														= _serverList;

//
// singleton
//
+ (DataCenter*)sharedDataCenter
{
	if(!globalDataCenter)
		globalDataCenter													= [[DataCenter alloc] init];
	
	return globalDataCenter;
}

//
// init
//
- (id)init
{
	//
	// call super
	//
	self																	= [super init];
	if(!self)
		return nil;
	
	return self;
}

//
// dealloc
//
- (void)dealloc
{
	self.database															= nil;
	self.domain																= nil;
	self.mailAddress														= nil;
	self.passKey															= nil;
	self.cookieNexusPhp														= nil;
	self.pushConfig															= nil;
	self.serverList															= nil;
	
	[super dealloc];
}

//
// data file path
//
- (NSString*)dataFilePath:(BOOL)documentPath nameFormat:(NSString*)nameFormat,...
{
	NSString* pathName														= [NSSearchPathForDirectoriesInDomains(documentPath ? NSDocumentDirectory : NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
	if(![[NSFileManager defaultManager] fileExistsAtPath:pathName isDirectory:0])
		[[NSFileManager defaultManager] createDirectoryAtPath:pathName withIntermediateDirectories:YES attributes:nil error:0];
	
	va_list argList;
	va_start(argList, nameFormat);
	NSString* fileName														= [[[NSString alloc] initWithFormat:nameFormat arguments:argList] autorelease];
	va_end(argList);
	
	return [pathName stringByAppendingPathComponent:fileName];
}

//
// user info file path
//
- (NSString*)userInfoFilePath
{
	return [self dataFilePath:YES nameFormat:@"%@.user.plist", self.domain];
}

//
// push config file path
//
- (NSString*)pushConfigFilePath
{
	return [self dataFilePath:YES nameFormat:@"%@.push.plist", self.domain];
}

//
// server config list file path
//
- (NSString*)serverConfigListFilePath
{
	return [self dataFilePath:YES nameFormat:@"%@.server.list.plist", self.domain];
}

//
// check database table structure
//
- (void)checkDatabase
{
	//
	// create torrent table
	//
	NSString* sqlStatement													= @"CREATE TABLE IF NOT EXISTS torrents("
																				@"torrent_id INTEGER PRIMARY KEY ASC,"
																				@"category VARCHAR(32) NOT NULL DEFAULT '',"
																				@"title VARCHAR(255) NOT NULL DEFAULT '',"
																				@"size VARCHAR(128) NOT NULL DEFAULT '',"
																				@"upload_date VARCHAR(128) NOT NULL DEFAULT '',"
																				@"up_rate VARCHAR(32),"
																				@"down_rate VARCHAR(32),"
																				@"magic_expire VARCHAR(128),"
																				@"unread INTEGER UNSIGNED NOT NULL DEFAULT 1"
																			  @")";
	
	SQLiteStatement* statement												= [self.database prepareStatement:@"%@", sqlStatement];
	[statement fetch];
	
	sqlStatement															= @"CREATE TABLE IF NOT EXISTS files("
																				@"torrent_id INTEGER UNSIGNED NOT NULL DEFAULT 0,"
																				@"file_name VARCHAR(255) NOT NULL DEFAULT '',"
																				@"file_size VARCHAR(32) NOT NULL DEFAULT '',"
																				@"PRIMARY KEY (torrent_id, file_name)"
																			  @")";
	
	statement																= [self.database prepareStatement:@"%@", sqlStatement];
	[statement fetch];
	
	sqlStatement															= @"CREATE INDEX IF NOT EXISTS files_index ON files (torrent_id)";	
	statement																= [self.database prepareStatement:@"%@", sqlStatement];
	[statement fetch];
}

//
// server from server info
//
- (id<TorrentClient>)serverFromServerInfo:(ServerInfo*)serverInfo
{
	if([serverInfo.serverType isEqualToString:@"rtorrent-xmlrpc"])
		return [[[RtorrentXmlRpcClient alloc] initWithServerInfo:serverInfo] autorelease];
	
	if([serverInfo.serverType isEqualToString:@"deluge-web-jsonrpc"])
		return [[[DelugeWebJsonRpcClient alloc] initWithServerInfo:serverInfo] autorelease];
	
	return nil;
}

//
// add server
//
- (void)addServerFromServerInfo:(ServerInfo*)serverInfo
{
	if(!self.serverList)
		self.serverList														= [NSMutableArray arrayWithCapacity:1];
	
	[self.serverList addObject:[self serverFromServerInfo:serverInfo]];
}

//
// update server at index
//
- (id<TorrentClient>)updateServerAtIndex:(NSInteger)index fromServerInfo:(ServerInfo*)serverInfo
{
	if(index < 0 || index >= self.serverList.count)
		return nil;
	
	id<TorrentClient> retValue												= [self.serverList objectAtIndex:index];
	if([[retValue serverInfo].serverType isEqualToString:serverInfo.serverType])
	{
		[retValue setServerInfo:serverInfo];
		return retValue;
	}
	
	retValue																= [self serverFromServerInfo:serverInfo];
	[self.serverList replaceObjectAtIndex:index withObject:retValue];
	return retValue;
}

//
// remove server at index
//
- (void)removeServerAtIndex:(NSInteger)index
{
	if(index >= 0 && index < self.serverList.count)
		[self.serverList removeObjectAtIndex:index];
}

//
// load server list
//
- (void)loadServerList
{
	NSArray* serverConfigList												= [NSArray arrayWithContentsOfFile:[self serverConfigListFilePath]];
	for(NSDictionary* serverConfig in serverConfigList)
		[self addServerFromServerInfo:[[[ServerInfo alloc] initWithConfig:serverConfig] autorelease]];
}

//
// save server list
//
- (void)saveServerList
{
	NSMutableArray* serverConfigList										= [NSMutableArray arrayWithCapacity:self.serverList.count];
	for(id<TorrentClient> torrentClient in self.serverList)
		[serverConfigList addObject:[[torrentClient serverInfo] configDictionary]];
	
	[serverConfigList writeToFile:[self serverConfigListFilePath] atomically:YES];
}

//
// load
//
- (void)loadDomain:(NSString*)domain
{
	//
	// initialize
	//
	self.domain																= domain;
	
	//
	// load user info
	//
	NSDictionary* dictionary												= [NSDictionary dictionaryWithContentsOfFile:[self userInfoFilePath]];
	self.userId																= [[dictionary valueForKey:@"userId"] integerValue];
	self.mailAddress														= [dictionary valueForKey:@"mailAddress"];
	self.passKey															= [dictionary valueForKey:@"passKey"];
	self.cookieNexusPhp														= [dictionary valueForKey:@"cookieNexusPhp"];
	
	//
	// load database
	//
	self.database															= [[[SQLiteDatabase alloc] initWithFileName:[self dataFilePath:NO nameFormat:@"%@.db", self.domain]] autorelease];
	[self checkDatabase];
	
	//
	// load push config file
	//
	self.pushConfig															= [[[PushConfig alloc] initFromFile:[self pushConfigFilePath]] autorelease];
	if(!self.pushConfig.apnProvider.length && !self.pushConfig.categoryFilter.count)
		self.pushConfig														= [self defaultPushConfig];
	
	//
	// load server list
	//
	[self loadServerList];
}

//
// invalid user info
//
- (void)invalidUserInfo
{
	self.userId																= 0;
	self.mailAddress														= nil;
	self.cookieNexusPhp														= nil;
	self.passKey															= nil;
}

//
// save user info
//
- (void)saveUserInfo
{
	NSMutableDictionary* dictionary											= [NSMutableDictionary dictionaryWithCapacity:4];
	[dictionary setValue:[NSNumber numberWithInteger:self.userId] forKey:@"userId"];
	[dictionary setValue:self.mailAddress forKey:@"mailAddress"];
	[dictionary setValue:self.passKey forKey:@"passKey"];
	[dictionary setValue:self.cookieNexusPhp forKey:@"cookieNexusPhp"];
	[dictionary writeToFile:[self userInfoFilePath] atomically:YES];
}

//
// restore push config
//
- (PushConfig*)defaultPushConfig
{
	return [[[PushConfig alloc] initFromFile:[[NSBundle mainBundle] pathForResource:@"default.push" ofType:@"plist"]] autorelease];
}

//
// save push config
//
- (void)savePushConfig
{
	[self.pushConfig saveToFile:[self pushConfigFilePath]];
}

//
// torrent info
//
- (TorrentInfo*)torrentInfoFromId:(NSInteger)torrentId
{
	SQLiteStatement* statement												= [self.database prepareStatement:@"SELECT * FROM torrents WHERE torrent_id=%d", torrentId];
	if(![statement fetch])
		return nil;
	
	TorrentInfo* info														= [[[TorrentInfo alloc] init] autorelease];
	info.torrentId															= torrentId;
	info.category															= [statement stringValue:1];
	info.title																= [statement stringValue:2];
	info.size																= [statement stringValue:3];
	info.uploadDate															= [statement stringValue:4];
	info.upRate																= [statement stringValue:5];
	info.downRate															= [statement stringValue:6];
	info.magicExpire														= [statement stringValue:7];
	info.unread																= [statement intValue:8];
	info.seederCount														= -1;
	info.leecherCount														= -1;
	
	return info;
}

//
// add torrent
//
- (BOOL)checkAddTorrent:(TorrentInfo*)info
{
	//
	// check exist
	//
	BOOL updateDatabase														= NO;
	SQLiteStatement* statement												= [self.database prepareStatement:@"SELECT unread FROM torrents WHERE torrent_id=%d AND category=%@ AND title=%@ AND size=%@ AND upload_date=%@ AND up_rate %@%@ AND down_rate %@%@ AND magic_expire %@%@",
																												info.torrentId, [SQLiteDatabase escapeString:info.category], [SQLiteDatabase escapeString:info.title], [SQLiteDatabase escapeString:info.size], [SQLiteDatabase escapeString:info.uploadDate],
																												info.upRate ? @"=" : @"is", [SQLiteDatabase escapeString:info.upRate], info.downRate ? @"=" : @"is", [SQLiteDatabase escapeString:info.downRate],
																												info.magicExpire ? @"=" : @"is", [SQLiteDatabase escapeString:info.magicExpire]];
	if([statement fetch])
		info.unread															= [statement intValue:0];
	else
		updateDatabase														= YES;

	if(!updateDatabase)
		return NO;
	
	statement																= [self.database prepareStatement:@"REPLACE INTO torrents "
																												@"(torrent_id, category, title, size, upload_date, up_rate, down_rate, magic_expire, unread) VALUES "
																												@"(%d,			%@,		  %@,	 %@,		%@,			%@,		%@,			%@,			%d)",
																												info.torrentId, [SQLiteDatabase escapeString:info.category], [SQLiteDatabase escapeString:info.title], [SQLiteDatabase escapeString:info.size], [SQLiteDatabase escapeString:info.uploadDate],
																												[SQLiteDatabase escapeString:info.upRate], [SQLiteDatabase escapeString:info.downRate], [SQLiteDatabase escapeString:info.magicExpire], info.unread];
	[statement fetch];
	return updateDatabase;
}

//
// mark as read
//
- (void)markAsReadForTorrentId:(NSInteger)torrentId
{
	SQLiteStatement* statement												= nil;
	if(torrentId)
		statement															= [self.database prepareStatement:@"UPDATE torrents SET unread=0 where torrent_id=%d", torrentId];
	else
		statement															= [self.database prepareStatement:@"UPDATE torrents SET unread=0"];
	
	[statement fetch];
}

//
// get file list
//
- (NSArray*)fileListForTorrentId:(NSInteger)torrentId
{
	SQLiteStatement* statement												= [self.database prepareStatement:@"SELECT * FROM files WHERE torrent_id=%d", torrentId];
	NSMutableArray* retValue												= nil;
	while([statement fetch])
	{
		@autoreleasepool
		{
			if(!retValue)
				retValue													= [[NSMutableArray alloc] initWithCapacity:10];
			
			TorrentFileInfo* info											= [[[TorrentFileInfo alloc] init] autorelease];
			info.fileName													= [statement stringValue:1];
			info.fileSize													= [statement stringValue:2];
			[retValue addObject:info];
		}
	}
	
	return [retValue autorelease];
}

//
// add file info
//
- (void)addFileInfo:(TorrentFileInfo*)info forTorrentId:(NSInteger)torrentId
{
	SQLiteStatement* statement												= [self.database prepareStatement:@"REPLACE INTO files(torrent_id,file_name,file_size) VALUES (%d,%@,%@)",
																												torrentId, [SQLiteDatabase escapeString:info.fileName], [SQLiteDatabase escapeString:info.fileSize]];
	[statement fetch];
}

@end
